package com.cl.code.database.mybatis.config;

import com.cl.code.database.mybatis.BaseMapper;
import com.cl.code.database.mybatis.FunctionMapper;
import com.cl.code.database.mybatis.constant.CLMybatisConstants;
import com.cl.code.database.mybatis.module.page.PageParam;
import com.cl.code.database.mybatis.module.result.ListResultWrapper;
import com.cl.code.database.mybatis.module.result.PageResultWrapper;
import com.cl.code.database.mybatis.module.result.SingleResultWrapper;
import com.cl.code.database.mybatis.module.sql.Select;
import com.cl.code.database.mybatis.module.sql.View;
import com.cl.code.database.mybatis.module.sql.Where;
import com.cl.code.database.mybatis.module.sql.table.*;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.core.GenericTypeResolver;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * @author chengliang
 * @since 1.0.0
 */
public class CLMybatisMapperFactoryBean<Mapper extends BaseMapper<Entity>, Entity> extends MapperFactoryBean<Mapper> {

    private Class<Entity> entityClass;

    public CLMybatisMapperFactoryBean() {
        super();
    }

    @SuppressWarnings("unchecked")
    public CLMybatisMapperFactoryBean(Class<Mapper> mapperInterface) {
        super(mapperInterface);
        this.entityClass = (Class<Entity>) Objects.requireNonNull(GenericTypeResolver.resolveTypeArguments(mapperInterface, BaseMapper.class))[0];
    }

    @Override
    @SuppressWarnings("unchecked")
    public Mapper getObject() throws Exception {
        Mapper mapper = super.getObject();
        if (mapper != null) {
            // 自定义处理
            // 代理代理 然后实现自己的逻辑
            FunctionMapper<Entity> pm = new FunctionMapper<>() {
                @Override
                public Class<Entity> getEntityClass() {
                    return entityClass;
                }

                @Override
                public Select<Entity> createSelect() {
                    return Select.of(getEntityClass());
                }

                @Override
                public Where<Entity> createWhere() {
                    return Where.of(getEntityClass());
                }

                @Override
                public View<Entity> createView() {
                    return View.of(getEntityClass());
                }

                @Override
                public ListResultWrapper<Entity> list() {
                    List<Entity> list = mapper.list(null, null, null);
                    return ListResultWrapper.of(list);
                }

                @Override
                public ListResultWrapper<Entity> list(Select<Entity> select) {
                    List<Entity> list = mapper.list(select.view(), select.where(), select.order());
                    return ListResultWrapper.of(list);
                }

                @Override
                public PageResultWrapper<Entity> page(Select<Entity> select, int pageNumber, int pageSize) {
                    return mapper.page(select, PageParam.of(pageNumber, pageSize));
                }

                @Override
                public PageResultWrapper<Entity> page(Select<Entity> select, PageParam pageParam) {
                    pageParam.startPage();
                    List<Entity> list = mapper.list(select.view(), select.where(), select.order());
                    return PageResultWrapper.of(list);
                }

                @Override
                public SingleResultWrapper<Entity> single(Serializable id) {
                    if (id == null) {
                        throw new IllegalArgumentException("id can not be null");
                    }
                    TableField idField = TableInfoFactory.getTableInfo(entityClass).getIdField();
                    Select<Entity> select = createSelect();
                    select.where().equalTo(idField.getFieldName(), id);
                    return mapper.single(select);
                }

                @Override
                public SingleResultWrapper<Entity> single(Select<Entity> select) {
                    List<Entity> list = mapper.list(select.view(), select.where(), select.order());
                    if (list != null && !list.isEmpty()) {
                        return SingleResultWrapper.of(list.getFirst());
                    } else {
                        return SingleResultWrapper.of(null);
                    }
                }

                @Override
                public int count() {
                    return mapper.count(null);
                }

                @Override
                public int insert(Entity entity) {
                    TableInfo tableInfo = TableInfoFactory.getTableInfo(entityClass);
                    if (tableInfo.isSupportVersion()) {
                        VersionField versionField = tableInfo.getVersionField();
                        versionField.setValue(entity, 1);
                    }
                    if (tableInfo.isSupportLogicDelete()) {
                        LogicDeleteField logicDeleteField = tableInfo.getLogicDeleteField();
                        Object value = logicDeleteField.getValue(entity, logicDeleteField.getType());
                        if (value == null) {
                            logicDeleteField.setValue(entity, logicDeleteField.getNotDeleteTag());
                        }
                    }
                    return mapper.insert(entity);
                }

                @Override
                public int insertBatch(Collection<Entity> entityList) {
                    if (entityList == null || entityList.isEmpty()) {
                        return 0;
                    } else {
                        return mapper.insertBatch(entityList);
                    }
                }

                @Override
                public int updateById(Entity entity) {
                    Where<Entity> where = createWhere();
                    TableField idField = TableInfoFactory.getTableInfo(entityClass).getIdField();
                    Serializable id = idField.getValue(entity, Serializable.class);
                    if (id == null) {
                        throw new IllegalArgumentException("id can not be null");
                    }
                    where.equalTo(idField.getFieldName(), id);
                    return mapper.update(entity, where);
                }

                @Override
                public int updateByIdSelective(Entity entity) {
                    Where<Entity> where = createWhere();
                    TableField idField = TableInfoFactory.getTableInfo(entityClass).getIdField();
                    Serializable id = idField.getValue(entity, Serializable.class);
                    if (id == null) {
                        throw new IllegalArgumentException("id can not be null");
                    }
                    where.equalTo(idField.getSqlFieldName(), id);
                    return mapper.updateSelective(entity, where);
                }

                @Override
                public int deleteById(Serializable id) {
                    if (id == null) {
                        throw new IllegalArgumentException("id can not be null");
                    }
                    TableField idField = TableInfoFactory.getTableInfo(entityClass).getIdField();
                    Where<Entity> where = createWhere();
                    where.equalTo(idField.getFieldName(), id);
                    return mapper.delete(where);
                }

                @Override
                public int deleteByIds(Collection<Serializable> ids) {
                    if (ids == null || ids.isEmpty()) {
                        throw new IllegalArgumentException("ids can not be null or empty");
                    }
                    TableField idField = TableInfoFactory.getTableInfo(entityClass).getIdField();
                    Where<Entity> where = createWhere();
                    where.in(idField.getFieldName(), ids);
                    return mapper.delete(where);
                }

                @Override
                public int logicDeleteById(Serializable id) {
                    if (id == null) {
                        throw new IllegalArgumentException("id can not be null");
                    }
                    TableInfo tableInfo = TableInfoFactory.getTableInfo(entityClass);
                    if (!tableInfo.isSupportLogicDelete()) {
                        throw new UnsupportedOperationException("table not support logic delete");
                    }
                    try {
                        TableField idField = tableInfo.getIdField();
                        TableField logicDeleteField = tableInfo.getLogicDeleteField();
                        Entity entity = entityClass.getDeclaredConstructor().newInstance();
                        logicDeleteField.setValue(entity, CLMybatisConstants.DELETED);
                        Where<Entity> where = Where.of(entityClass);
                        where.equalTo(idField.getFieldName(), id);
                        return mapper.updateSelective(entity, where);
                    } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
                             InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }

                @Override
                public int logicDeleteByIds(Collection<Serializable> ids) {
                    if (ids == null || ids.isEmpty()) {
                        throw new IllegalArgumentException("ids can not be null or empty");
                    }
                    TableInfo tableInfo = TableInfoFactory.getTableInfo(entityClass);
                    if (!tableInfo.isSupportLogicDelete()) {
                        throw new UnsupportedOperationException("table not support logic delete");
                    }
                    try {
                        TableField idField = tableInfo.getIdField();
                        TableField logicDeleteField = tableInfo.getLogicDeleteField();
                        Entity entity = entityClass.getDeclaredConstructor().newInstance();
                        logicDeleteField.setValue(entity, CLMybatisConstants.DELETED);
                        Where<Entity> where = Where.of(entityClass);
                        where.in(idField.getFieldName(), ids);
                        return mapper.updateSelective(entity, where);
                    } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
                             InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            return (Mapper) Proxy.newProxyInstance(mapper.getClass().getClassLoader(), new Class[]{getObjectType()},
                    (proxy, method, args) -> {
                        if (method.getDeclaringClass().isAssignableFrom(FunctionMapper.class)) {
                            return method.invoke(pm, args);
                        } else {
                            return method.invoke(mapper, args);
                        }
                    });
        } else {
            return null;
        }
    }


}
